<?php

/**
 * \file
 * Classe `Member`.
 */
 
namespace pilar\Objects /* \cond */ ; /* \endcond */
{

/**
 * Membre d'un objet d'une entité abstraite.
 *
 * \since 07/10/2008
 * \author Matthieu OVIEDO <matthieu.oviedo@gmail.com>
 */
class Member {

/**
 * Objet parent.
 *
 * \since 07/10/2008
 */
private $_parentObject;


/**
 * Membre d'entité type du membre.
 *
 * \since 07/10/2008
 */
private $_entityMember;


/**
 * Champs instanciés du membre.
 *
 * \since 07/10/2008
 */
private $_fields;


/**
 * Constructeur.
 *
 * \since 07/10/2008
 * \param $entityMember Membre d'entité type du membre.
 */
public function __construct($entityMember) {
 $this->_parentObject = null;
 $this->_entityMember = $entityMember;
 $this->_fields = array();
}


/**
 * Accède à un sous élément du membre en fonction de son nom.
 *
 * Par défaut les éléments sont les champs du membre. Néanmoins 
 * cela peut être surchargé et dépendre du type de membre rencontré.
 *
 * Par exemple :
 *
 * \code
 * // On suppose que $toto est un objet.
 *
 * // Récupération de la valeur du champ 'bar' du membre 'foo'
 * // de l'objet $toto :
 *  $value = $toto->foo->bar->get();
 * \endcode
 *
 * \param $name Nom identifiant l'élément.
 * \return Élément trouvé.
 *
 * \see field
 *
 * \since 07/10/2008
 */
public function __get($name) {
 return $this->field($name);
}


/**
 * Soi-même.
 *
 * \since 08/03/2009
 */
public function __getLink() {
 return $this;
}


/**
 * Permet de spécifier l'objet parent du membre.
 *
 * \since 07/10/2008
 * \param $object Objet parent.
 */
public function setParentObject($object) {
 $this->_parentObject = $object;
}


/**
 * Objet parent.
 *
 * \since 07/10/2008
 * \return Objet parent.
 */
public function parentObject() {
 return $this->_parentObject;
}


/**
 * Membre d'entité type du membre.
 *
 * \since 07/10/2008
 * \return Le membre d'entité.
 */
public function entityMember() {
 return $this->_entityMember;
}


/**
 * Accède à un champ du membre par son nom et le retourne.
 *
 * Un chargement dynamique avec cache est géré en interne.
 *
 * \param $name Nom du champ requis. Par défaut \c 'value'.
 * \return Le champ requis.
 *
 * \since 07/03/2009
 */
public function field($name = 'value') {
 // Si le champ a déjà été chargé on retourne directement son
 // instance :
  if(isset($this->_fields[$name])) {
   return $this->_fields[$name];
  }
  
 // A ce stade il faut instancier un nouveau champ.
 
 // Evaluation du champ de l'entité :
  $entityField = $this->_entityMember->field($name);
  
 // Création du nouveau champ d'objet :
  $field = new Field($entityField);
  
 // On s'associe au champ en tant que membre parent :
  $field->setParentMember($this);
 
 // Ajout du champ à la liste locale :
  $this->_fields[$name] = $field;
  
 return $field;
}


/**
 * Assigne une valeur à un champ.
 *
 * \param $name Nom du champ.
 * \param $value Valeur à assigner.
 *
 * \see field
 *
 * \since 26/02/2009
 */
public function __set($name, $value) {
 $this->field($name)->set($value);
}


/**
 * Valeur du champ principal (i.e de nom \c 'value') du membre.
 *
 * \since 07/10/2008
 * \return Valeur du champ.
 *
 * \see field
 */
public function get() {
 return $this->field('value')->get();
}


/**
 * Change les valeurs des champs du membre.
 *
 * \since 07/10/2008
 * \param $values Arbre des valeurs.
 * \return Instance de l'objet parent.
 */
public function set($values) {
 // Si la valeur est un scalaire on l'assigne au champ principal :
  if(!is_array($values)) return $this->field('value')->set($values);

 // Sinon on assigne successivement les valeurs aux champs :
  foreach($values as $fieldName => $value) {
   $this->field($fieldName)->set($value);
  }
 
 return $this->_parentObject;
}


/**
 * Description DOM du membre.
 *
 * \param $domDoc \DOMDocument a utiliser comme document parent.
 * \return Noeud DOM de la description du membre.
 *
 * \since 07/10/2008
 */
public function domNode_meta($domDoc) {
 $domMember = $domDoc->createElement('member');

 $domMember->setAttribute('name', $this->_entityMember->get('name'));

 foreach($this->_fields as $field) {
  $domMember->appendChild($field->domNode_meta($domDoc));
 }

 return $domMember;
}


/**
 * Nom du membre.
 *
 * \return Nom du membre.
 *
 * \since 15/05/2009
 */
public function name() {
 return $this->_entityMember->param('name');
}


/**
 * Version DOM du membre.
 *
 * \param $domDoc \DOMDocument a utiliser comme document parent.
 * \return Noeud DOM du membre.
 *
 * \since 10/12/2008
 */
public function domNode($domDoc) {
 $domMember = $domDoc->createElement($this->name());

 foreach($this->_fields as $field) {
  $domMember->appendChild($field->domNode($domDoc));
 }

 return $domMember;
}


/**
 * Version DOM 'générique' du membre.
 *
 * \param $domDoc \DOMDocument a utiliser comme document parent.
 * \return Attribut DOM générique du membre.
 * \since 03/03/2009
 */
public function domNode_generic($domDoc) {
	$domMember = $domDoc->createElement('Member');
	$domMember->setAttribute('name', $this->_entityMember->get('name'));
	
 foreach($this->_fields as $field) {
  $domMember->appendChild($field->domNode($domDoc));
 }

 return $domMember;
}


/**
 * Flush le membre.
 *
 * \since 07/10/2008
 */
public function flush() {
 foreach($this->_fields as $field) {
  $field->flush();
 }
}


/**
 * Remet à zéro le membre.
 *
 * \since 07/10/2008
 */
public function reset() { 
 foreach($this->_fields as $field) {
  $field->reset();
 }
}


/**
 * Permet de synchroniser le membre avec sa représentation en base de données.
 *
 * \since 07/10/2008
 */
public function sync($values) { 
 // Si la valeur est un scalaire on l'assigne au champ principal :
  if(!is_array($values)) {
   $this->field('value')->sync($values);
   return;
  }
  
 // Sinon on assigne successivement les valeurs aux champs :
  foreach($values as $fieldName => $value) {
   $this->field($fieldName)->sync($value);
  }
}


/**
 * Champs du membre.
 *
 * \since 07/10/2008
 * \return Champs du membre sous la forme <tt>{ $fieldName => $field }</tt>.
 */
public function fields() {
 return $this->_fields;
}


/**
 * Appelée lorsque l'objet parent est sauvegardé.
 *
 * \since 08/03/2009
 */
public function onSave() {
}

} // Class.

} // Namespace.

?>
